<template>
    <van-popup v-model="isShow" position="bottom" @close="hide">
    <div class="calendar">
        <div class="van-hairline--top-bottom van-picker__toolbar">
            <div class="van-picker__cancel" @click="hide">取消</div>
            <div class="van-picker__confirm" @click="confirm">确认</div>
        </div>
        <div class="calendar-tools">
            <span class="calendar-prev" @click="prev">
                <svg width="20" height="20" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                <g class="transform-group">
                    <g transform="scale(0.015625, 0.015625)">
                        <path d="M671.968 912c-12.288 0-24.576-4.672-33.952-14.048L286.048 545.984c-18.752-18.72-18.752-49.12 0-67.872l351.968-352c18.752-18.752 49.12-18.752 67.872 0 18.752 18.72 18.752 49.12 0 67.872l-318.016 318.048 318.016 318.016c18.752 18.752 18.752 49.12 0 67.872C696.544 907.328 684.256 912 671.968 912z" fill="#5e7a88"></path>
                    </g>
                </g>
                </svg>
            </span>
            <span class="calendar-next"  @click="next">
                <svg width="20" height="20" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                <g class="transform-group">
                    <g transform="scale(0.015625, 0.015625)">
                        <path d="M761.056 532.128c0.512-0.992 1.344-1.824 1.792-2.848 8.8-18.304 5.92-40.704-9.664-55.424L399.936 139.744c-19.264-18.208-49.632-17.344-67.872 1.888-18.208 19.264-17.376 49.632 1.888 67.872l316.96 299.84-315.712 304.288c-19.072 18.4-19.648 48.768-1.248 67.872 9.408 9.792 21.984 14.688 34.56 14.688 12 0 24-4.48 33.312-13.44l350.048-337.376c0.672-0.672 0.928-1.6 1.6-2.304 0.512-0.48 1.056-0.832 1.568-1.344C757.76 538.88 759.2 535.392 761.056 532.128z" fill="#5e7a88"></path>
                    </g>
                </g>
                </svg>
            </span>
            <div class="calendar-info">
                <!-- {{monthString}} -->
                <div class="month">
                    <div class="month-inner" :style="{'top':-(this.month*20)+'px'}">
                        <span v-for="(m,index) in Months" :key="index">{{m}}</span>
                    </div>
                </div>
                <div class="year">{{year}}</div>
            </div>
        </div>
        <table>
        <thead>
            <tr>
                <td v-for="(week,index) in Weeks" :key="index" class="week">{{week}}</td>
            </tr>
        </thead>
        <tbody>
        <tr v-for="(line,index1) in days" :key="index1">
            <td v-for="(day,index2) in line" :key="index2" :class="{'today':isToday(day),'disabled':isDisabled(day)}" @click="onDayClick(day,day.data!=undefined)">
                <div class="top">
                    <span class="day" :class="{inRange:isRangeDay(day)}" v-if="!isToday(day)">{{day.date}}</span>
                    <span class="badge day" v-else>今</span>
                    <span class="badge selected" v-if="isSelectedDate(day)">{{dateText}}</span>
                    <span class="badge begin" v-else-if="isSelectedBegin(day)">{{beginText}}</span>
                    <span class="badge end" v-else-if="isSelectedEnd(day)">{{endText}}</span>
                </div>
                <div class="bottom" v-if="day.data!=undefined">
                    <slot :data="day.data"></slot>
                </div>
            </td>
        </tr>
        </tbody>
        </table>
    </div>
    </van-popup>
</template>

<script>
const Model = {SINGLE:"single",RANGE:"range"}
export default {
    name: "Calendar",
    props: {
        // 模式：single，range
        model: {
            type: String,
            default: Model.DAY
        },
        // 没有数据的日期也可以选择
        selectableNoData: {
            type: Boolean,
            default: true
        },
        // 选择的日期或起止日期范围
        selected: [String,Array],
        text: [String,Array]
    },
    data() {
        return {
            isShow: false,
            //常量，用于遍历显示
            Weeks: ['日', '一', '二', '三', '四', '五', '六'],
            Months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
            //当前日历显示年份
            year: 0,
            //当前日历显示月份
            month: 0,
            //系统今天日期
            today:[],
            //设定数据
            datas: [],
            //生成的当前页要显示的所有日期数据
            days:[],
            //初始化选中的日期或起止日期
            date:'',
            dateText:'',
            rangeSetStep:1,
            begin:'',
            beginText:'',
            end:'',
            endText:''
        }
    },
    mounted() {
        this.init()
    },
    watch: {
        datas(n){
            this.render()
            window.console.log(n)
        }
    },
    methods: {
        show(){
            this.isShow = true
        },
        hide(){
            this.isShow = false
        },
        confirm(){
            if(this.model==Model.SINGLE){
                this.$emit("confirm",this.date)
            }else if(this.model==Model.RANGE){
                this.$emit("confirm",[this.begin,this.end])
            }
            this.hide()
        },
        setDatas(datas){
            this.datas = datas
            this.render()
        },
        init(){
            //初始化今天日期
            let now = new Date();
            this.year = now.getFullYear()
            this.month = now.getMonth()
            let day = now.getDate()
            this.today = [this.year,this.month,day]
            //初始化默认选择日期或起止日期
            if(this.model==Model.SINGLE){
                this.date = this.selected
                if(this.text){
                    this.dateText = this.text
                }else{
                    this.dateText = '游'
                }
            }else if(this.model==Model.RANGE){
                this.rangeSetStep = 1
                this.begin = this.selected[0]
                this.end = this.selected[1]
                if(this.text&&this.text.length==2){
                    this.beginText = this.text[0]
                    this.endText = this.text[1]
                }else{
                    this.beginText = '住'
                    this.endText = '离'
                }
            }
            //渲染视图
            this.render()
            this.$emit("ready",this)
            //初始化时触发一次日期和月份变化事件
            this.emitDateChange()
            this.emitMonthChange()
        },
        // 获取自定义数据
        getDatas(y,m,d){
            if(this.datas.length==0)return null;
            for(let index=0;index<this.datas.length;index++){
                let data = this.datas[index]
                if(data.date===`${y}-${this.zeroPad(m+1)}-${this.zeroPad(d)}`){
                    return data
                }
            }
            return null
        },
        // 渲染日期
        render() {
            let thisMonthFirstDay = new Date(this.year, this.month, 1).getDay()         //当月第一天
            let thisMonthLastDate = new Date(this.year, this.month + 1, 0).getDate()    //当月最后一天
            let lastMonthLastDate = new Date(this.year, this.month, 0).getDate()     //最后一月的最后一天
            //分别计算上月、本月、下月在本页中显示的起始和结束日期
            let lastMonthBeginDateInThisPage = lastMonthLastDate-thisMonthFirstDay+1,
                lastMonthEndDateInThisPage = lastMonthLastDate,
                thisMonthBeginDateInThisPage = 1,
                thisMonthEndDateInThisPage = thisMonthLastDate,
                nextMonthBeginDateInThisPage = 1,
                nextMonthEndDateInThisPage = 6*7-(lastMonthEndDateInThisPage-lastMonthBeginDateInThisPage+1)-(thisMonthEndDateInThisPage-thisMonthBeginDateInThisPage+1)
            //循环生成本页中要显示的日期，并标识出上月、本月、下月
            let i, temp = []
            let year = this.computedPrevYear(), month = this.computedPrevMonth()
            for(i = lastMonthBeginDateInThisPage; i <= lastMonthEndDateInThisPage; i++){
                temp.push({
                    date: i, flag: 1,
                    year, month, day: i,
                    data: this.getDatas(year,month,i),
                })
            }
            year = this.year, month = this.month
            for(i = thisMonthBeginDateInThisPage; i <= thisMonthEndDateInThisPage; i++){
                temp.push({
                    date: i, flag: 2,
                    year:this.year, month:this.month, day: i,
                    data: this.getDatas(this.year,this.month,i),
                })
            }
            year = this.computedNextYear(), month = this.computedNextMonth()
            for(i = nextMonthBeginDateInThisPage; i <= nextMonthEndDateInThisPage; i++){
                temp.push({
                    date: i, flag: 3,
                    year, month, day: i,
                    data: this.getDatas(year,month,i),
                })
            }
            let line = 0
            this.days = []
            temp.map(day=>{
                if(!this.days[line])this.days[line] = []
                this.days[line].push(day)
                if(new Date(day.year, day.month, day.day).getDay()==6)line++
            })
        },
        isDisabled(day){
            let date1 = new Date(day.year,day.month,day.date),
                date2 = new Date(this.today[0],this.today[1],this.today[2])
            return day.disabled
                ||day.flag!=2
                ||Number(date1)<Number(date2)
        },
        isToday(day){
            return day.year==this.today[0]&&day.month==this.today[1]&&day.date==this.today[2]
        },
        isRangeDay(day){
            if(this.model==Model.SINGLE){
                return this.date&&this.compareTo(this.date,day)==0
            }else if(this.model==Model.RANGE){
                return this.begin&&this.end&&this.compareTo(this.begin,day)<=0&&this.compareTo(this.end,day)>=0
            }
        },
        isSelectedDate(day){
            //初始化默认选择日期或起止日期
            if(this.model==Model.SINGLE&&this.date!=undefined){
                return this.compareTo(this.date,day)==0
            }
        },
        isSelectedBegin(day){
            if(this.model==Model.RANGE&&this.begin!=undefined){
                return this.compareTo(this.begin,day)==0
            }
        },
        isSelectedEnd(day){
            if(this.model==Model.RANGE&&this.end!=undefined){
                return this.compareTo(this.end,day)==0
            }
        },
        onDayClick(day,hasData){
            //判断如果没有数据时，并且设置的没有数据不可选择时，直接返回，不处理
            if(!hasData&&!this.selectableNoData)return
            if(this.model==Model.SINGLE){
                this.date = `${day.year}-${day.month+1}-${day.date}`
                this.emitDateChange()
            }else if(this.model==Model.RANGE){
                if(this.rangeSetStep==1){
                    this.begin = `${day.year}-${day.month+1}-${day.date}`
                    this.end = ''
                    this.rangeSetStep = 2
                }else if(this.rangeSetStep==2){
                    //判断开始日期必须小于结束日期
                    let compared = this.compareTo(this.begin,day)
                    if(compared==0)return
                    this.end = `${day.year}-${day.month+1}-${day.date}`
                    if(compared>0){
                        let t = this.begin
                        this.begin = this.end
                        this.end = t
                    }
                    this.rangeSetStep = 1
                    this.emitDateChange()
                }
            }
        },
        compareTo(dateStr,day){
            let splited = dateStr.split("-"),
                date1 = new Date(parseInt(splited[0]),parseInt(splited[1])-1,parseInt(splited[2])),
                date2 = new Date(day.year,day.month,day.date)
            return Number(date1)-Number(date2)
        },
        computedPrevYear(){
            let value=this.year
            if(this.month-1<0){
                value--
            }
            return value
        },
        computedPrevMonth(){
            let value=this.month
            if(this.month-1<0){
                value=11
            }else{
                value--
            }
            return value
        },
        computedNextYear(){
            let value=this.year
            if(this.month+1>11){
                value++
            }
            return value
        },
        computedNextMonth(){
            let value=this.month
            if(this.month+1>11){
                value=0
            }else{
                value++
            }
            return value
        },
        // 上月
        prev(e) {
            e.stopPropagation()
            if (this.month == 0) {
                this.month = 11
                this.year = parseInt(this.year) - 1
            } else {
                this.month = parseInt(this.month) - 1
            }
            this.render()
            this.emitMonthChange()
        },
        //  下月
        next(e) {
            e.stopPropagation()
            if (this.month == 11) {
                this.month = 0
                this.year = parseInt(this.year) + 1
            } else {
                this.month = parseInt(this.month) + 1
            }
            this.render()
            this.emitMonthChange()
        },
        // 触发日期变化事件
        emitDateChange(){
            if(this.model==Model.SINGLE){
                this.$emit("date-changed",this.date)
            }else if(this.model==Model.RANGE){
                this.$emit("date-changed",this.begin,this.end)
            }
        },
        //触发月份变化事件
        emitMonthChange(){
            this.$emit("month-changed",this.year,this.month)
        },
        // 日期补零
        zeroPad(n){
            return String(n < 10 ? '0' + n : n)
        },
    }
}

</script>

<style lang="scss" scoped>
.calendar {
    margin:auto;
    width: 100%;
    min-width:300px;
    background: #fff;
    font-family: "PingFang SC","Hiragino Sans GB","STHeiti","Microsoft YaHei","WenQuanYi Micro Hei",sans-serif;
    user-select:none;
}

.calendar-tools{
    height:40px;
    font-size: 20px;
    line-height: 40px;
    color:#5e7a88;
}
.calendar-prev{
    width: 14.28571429%;
    float:left;
    text-align: center;
}
.calendar-info{
    padding-top: 3px;
    font-size:16px;
    line-height: 1.3;
    text-align: center;
}
.calendar-info>div.month{
    margin:auto;
    height:20px;
    width:100px;
    text-align: center;
    color:#5e7a88;
    overflow: hidden;
    position: relative;
}
.calendar-info>div.month .month-inner{
    position: absolute;
    left:0;
    top:0;
    height:240px;
    transition:top .5s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.calendar-info>div.month .month-inner>span{
    display: block;
    font-size: 14px;
    height:20px;
    width:100px;
    overflow: hidden;
    text-align: center;
}
.calendar-info>div.year{
   font-size:10px;
   line-height: 1;
   color:#999;
}
.calendar-next{
    width: 14.28571429%;
    float:right;
    text-align: center;
}

.calendar table {
    clear: both;
    width: 100%;
    border-collapse: collapse;
    color: #444444;
}
.calendar td {
    margin:2px !important;
    padding:0px 0;
    width: 14.28571429%;
    height:60px;
    text-align: center;
    vertical-align: middle;
    font-size:14px;
    line-height: 125%;
    position: relative;
    vertical-align: top;
    border:1px solid #eee;
}
.calendar td.week{
    font-size:10px;
    pointer-events:none !important;
    cursor: default !important;
}
.calendar td.disabled {
    color: #ccc;
    pointer-events:none !important;
    cursor: default !important;
}
.calendar td .top{
    float:left;
    width:100%;
    padding:2px 1px;
}
.calendar td span.badge{
    width:17px;
    height:17px;
    font-size: 12px;
    line-height:17px;
    border-radius:10px;
    color:#fff;
}
.calendar td span.day{
    float:left;
}
.calendar td span.day.inRange{
    color:#19be6b;
}
.calendar td.today span.day{
    background-color: #ed4014;
}
.calendar td span.badge.selected{
    float:right;
    background-color:#19be6b;
}
.calendar td span.badge.begin{
    float:right;
    background-color:#19be6b;
}
.calendar td span.badge.end{
    float:right;
    background-color:#19be6b;
}
.calendar td .bottom{
    float:left;
    width:100%;
    text-align:left;
    padding:0 2px;
    font-size:8px;
    color:#444;
}
.calendar td.today span.red{
    background-color: #ea6151;
    color: #fff;
}
.calendar td.today span.red:hover{
    background-color: #ea6151;
    color: #fff;
}
.calendar thead td {
  text-transform: uppercase;
  height:30px;
  vertical-align: middle;
}
</style>
